﻿using System;
using System.Linq;

using Centido.Core;

namespace Centido.Management.ConsoleManager
{
    /// <summary>
    ///    A generic console command for the pre-login mode.
    /// </summary>
    internal abstract class PreLoginCommandBase : Command
    {
        /// <summary>
        ///   The provider name parameter.
        /// </summary>
        public const string ProviderNameParameter = "provider";

        /// <summary>
        ///   The ODBC driver name parameter.
        /// </summary>
        public const string DriverNameParameter = "driver";

        /// <summary>
        ///   The ODBC driver name parameter (short name).
        /// </summary>
        public const string DriverNameShortParameter = "d";

        /// <summary>
        ///   The host parameter.
        /// </summary>
        public const string HostParameter = "host";

        /// <summary>
        ///   The host parameter (short name).
        /// </summary>
        public const string HostShortParameter = "h";

        /// <summary>
        ///   The user name parameter.
        /// </summary>
        public const string UserNameParameter = "user";

        /// <summary>
        ///   The user name parameter (short name).
        /// </summary>
        public const string UserNameShortParameter = "u";

        /// <summary>
        ///   The user password parameter.
        /// </summary>
        public const string UserPasswordParameter = "password";

        /// <summary>
        ///   The user password parameter (short name).
        /// </summary>
        public const string UserPasswordShortParameter = "p";

        /// <summary>
        ///   The schema name parameter.
        /// </summary>
        public const string SchemaParameter = "schema";

        /// <summary>
        ///   The schema name parameter (short name).
        /// </summary>
        public const string SchemaShortParameter = "s";

        /// <summary>
        ///   The connection string parameter.
        /// </summary>
        public const string ConnectionStringParameter = "connectionstring";

        /// <summary>
        ///   The connection string parameter (short name).
        /// </summary>
        public const string ConnectionStringShortParameter = "cs";

        /// <summary>
        ///   The port number parameter.
        /// </summary>
        public const string PortNumberParameter = "port";

        /// <summary>
        ///   The database name parameter.
        /// </summary>
        public const string DbNameParameter = "db";


        /// <summary>
        ///   The default value of the host parameter.
        /// </summary>
        public const string HostParameterDefaultValue = "localhost";


        /// <summary>
        ///   Checks the console command parameters.
        /// </summary>
        /// <param name="context">The console command execution context.</param>
        /// <returns>Returns true if everything is ok; otherwise, returns false.</returns>
        protected virtual bool CheckCommandParameters(CommandExecutionContext context)
        {
            if (context == null)
                throw new ArgumentNullException("context");

            // If no parameters were specified
            if (!context.CommandParameters.Any()
                // or if connection string is not specified
                || !context.ParameterValueIsNotEmpty(ConnectionStringParameter, ConnectionStringShortParameter)
                // ... and db name, user name, user password or driver are not specified
                    && (!context.ParameterValueIsNotEmpty(UserNameParameter, UserNameShortParameter)
                        || !context.ParameterValueIsNotEmpty(UserPasswordParameter, UserPasswordShortParameter)
                        || !context.ParameterValueIsNotEmpty(DbNameParameter, DbNameParameter)
                        || !context.ParameterValueIsNotEmpty(DriverNameParameter, DriverNameShortParameter))
                // or if provider name is not specified
                || !context.ParameterValueIsNotEmpty(ProviderNameParameter))
                return false;

            return true;
        }


        /// <summary>
        ///   Populates a connection string.
        /// </summary>
        /// <param name="context">The current command context.</param>
        /// <exception cref="ArgumentNullException">Thrown when the value of <paramref name="context"/> is null.</exception>
        /// <returns></returns>
        protected string PopulateConnectionString(CommandExecutionContext context)
        {
            if (context == null)
                throw new ArgumentNullException("context");

            // The provider name
            var providerName = context.GetParameterValue(ProviderNameParameter);

            // If no generic parameters were specified
            if (!context.ParameterValueIsNotEmpty(HostParameter, HostShortParameter) &&
                !context.ParameterValueIsNotEmpty(UserNameParameter, UserNameShortParameter) &&
                !context.ParameterValueIsNotEmpty(UserPasswordParameter, UserPasswordShortParameter) &&
                !context.ParameterValueIsNotEmpty(PortNumberParameter, PortNumberParameter) &&
                !context.ParameterValueIsNotEmpty(DbNameParameter, DbNameParameter) &&
                !context.ParameterValueIsNotEmpty(DriverNameParameter, DriverNameShortParameter))
            {
                return context.GetParameterValue(ConnectionStringParameter, ConnectionStringShortParameter);
            }

            ConnectionStringBuilder builder;

            try
            {
                builder = ConnectionStringBuilders.GetBuilder(providerName);
            }
            catch (ArgumentException e)
            {
                // Displaying the error message
                Console.WriteLine(e.Message);
                return null;
            }

            // If the entire connection string was specified
            if (context.ParameterValueIsNotEmpty(ConnectionStringParameter, ConnectionStringShortParameter))
                builder.Parse(context.GetParameterValue(ConnectionStringParameter, ConnectionStringShortParameter));

            // If the driver name was specified
            if (context.ParameterValueIsNotEmpty(DriverNameParameter, DriverNameShortParameter))
                builder.Driver = context.GetParameterValue(DriverNameParameter, DriverNameShortParameter);

            // If the host name was specified
            if (context.ParameterValueIsNotEmpty(HostParameter, HostShortParameter))
                builder.Host = context.GetParameterValue(HostParameter, HostShortParameter);

            // If no host parameter was specified
            if (string.IsNullOrEmpty(builder.Host))
                // Setting the host to the default value
                builder.Host = HostParameterDefaultValue;

            // If the port number was specified
            if (context.ParameterValueIsNotEmpty(PortNumberParameter, PortNumberParameter))
            {
                try
                {
                    builder.PortNumber = int.Parse(context.GetParameterValue(PortNumberParameter, PortNumberParameter));
                }
                catch (FormatException)
                {
                    Console.WriteLine(Messages.LoginCommandPortNumberFormatError);
                    return null;
                }
            }

            // If the db name was specified
            if (context.ParameterValueIsNotEmpty(DbNameParameter, DbNameParameter))
                builder.DbName = context.GetParameterValue(DbNameParameter, DbNameParameter);

            // If the user name was specified
            if (context.ParameterValueIsNotEmpty(UserNameParameter, UserNameShortParameter))
                builder.UserName = context.GetParameterValue(UserNameParameter, UserNameShortParameter);

            // If the user password was specified
            if (context.ParameterValueIsNotEmpty(UserPasswordParameter, UserPasswordShortParameter))
                builder.UserPassword = context.GetParameterValue(UserPasswordParameter, UserPasswordShortParameter);

            return builder.Build();
        }
    }
}
